#include "epd_port.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include <string.h>

static const char *TAG = "EPD_PORT";

#define R_BUSY() gpio_get_level(((epd_port_t *)port)->busy_io_num)
#define W_RESET(level) gpio_set_level(((epd_port_t *)port)->reset_io_num, level)
#define W_DC(level) gpio_set_level(((epd_port_t *)port)->dc_io_num, level)
#define W_CS(level) gpio_set_level(((epd_port_t *)port)->cs_io_num, level)

esp_err_t epd_port_init(epd_port_t *port)
{
    // 初始化GPIO DC Reset输出
    gpio_config_t io_conf = {
        .pin_bit_mask = (1ULL << port->dc_io_num) | (1ULL << port->reset_io_num),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE};

    gpio_config(&io_conf);
    // 初始化GPIO Busy 输入
    io_conf.pin_bit_mask = (1ULL << port->busy_io_num);
    io_conf.mode = GPIO_MODE_INPUT;
    gpio_config(&io_conf);

    W_RESET(1);

    // 初始化SPI总线和设备
    spi_bus_config_t bus_conf = {
        .sclk_io_num = port->sck_io_num,
        .mosi_io_num = port->sda_io_num,
        .miso_io_num = GPIO_NUM_NC,
        .quadhd_io_num = GPIO_NUM_NC,
        .quadwp_io_num = GPIO_NUM_NC,
        .max_transfer_sz = port->max_transfer_sz,
    }; // spi_bus configurations

    ESP_RETURN_ON_ERROR(spi_bus_initialize(port->host_id, &bus_conf, SPI_DMA_CH_AUTO), TAG, "Failed to initialize SPI bus");
    spi_device_interface_config_t devcfg = {
        .command_bits = 0,
        .address_bits = 0,
        .dummy_bits = 0,
        .mode = 0,
        .duty_cycle_pos = 128,
        .cs_ena_posttrans = 0,
        .cs_ena_pretrans = 0,
        .clock_speed_hz = port->clock_speed_hz,
        // .spics_io_num = GPIO_NUM_NC,
        .spics_io_num = port->cs_io_num,
        .queue_size = port->queue_size,
    }; // spi_device configurations
    ESP_RETURN_ON_ERROR(spi_bus_add_device(port->host_id, &devcfg, &port->dev_handle), TAG, "Failed to create SPI device");

    return ESP_OK;
}

esp_err_t epd_port_write(epd_port_t *port, uint8_t *pdata, size_t len, uint8_t dc)
{
    spi_transaction_t trans;
    memset(&trans, 0, sizeof(trans));
    W_DC(dc);
    trans.tx_buffer = pdata;
    trans.length = len * 8;
    return spi_device_transmit(port->dev_handle, &trans);
}

inline int epd_port_is_busy(epd_port_t *port)
{
    return R_BUSY() == 0;
}

inline esp_err_t epd_port_set_reset(epd_port_t *port, uint8_t level)
{
    return W_RESET(level);
}
